/*
 * SiFiSy - A File System Simulator
 * Copyright (C) 2010  SevenSoft
 *
 * This file is part of SiFiSy.
 *
 * SiFiSy is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * SiFiSy is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with SiFiSy.  If not, see <http://www.gnu.org/licenses/>.
 */
package sifisy.newgui;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Vector;
import javax.swing.*;
import javax.swing.border.TitledBorder;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.tree.DefaultTreeModel;
import sifisy.connections.ProjectManager;
import sifisy.core.Model;

/**
 * ProjectPanel is a panel that represents a SiFiSy Project
 * @author Luca Zanini
 * @version %I%, %G%
 * @createData 20/05/2010
 */
public class ProjectPanel extends JPanel {

    private int projectId;
    private String projectName;
    private ProjectManager projectManager;
    private SifisyTreeNode node;
    private ProjectComparePanel panelCompare;
    private JToolBar toolBar;
    private JButton buttonAddSimulation;
    private JButton buttonClose;
    private JButton buttonImportSimulation;
    private JTabbedPane tabbedPane;

    /**
     * Initialize class fields and settings
     */
    private void initComponents() {
        toolBar = new JToolBar("Project ToolBar");
        toolBar.setFloatable(false);
        buttonAddSimulation = new JButton(Model.translate("addSimulation"));
        buttonAddSimulation.setIcon(new ImageIcon(getClass().getResource("icons/small/newSimulation.png")));
        buttonImportSimulation = new JButton(Model.translate("importSimulation"));
        buttonImportSimulation.setIcon(new ImageIcon(getClass().getResource("icons/small/open.png")));
        buttonClose = new JButton(Model.translate("closeButton"));
        buttonClose.setIcon(new ImageIcon(getClass().getResource("icons/small/close.png")));

        toolBar.add(buttonAddSimulation);
        toolBar.add(buttonImportSimulation);
        toolBar.add(buttonClose);

        tabbedPane = new JTabbedPane();
        tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);

        panelCompare = new ProjectComparePanel(this);

        // Adding listeners
        buttonAddSimulation.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                new NewSimulationWidget(ProjectPanel.this);
            }
        });
        buttonImportSimulation.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                String filePath = MainWidget.showOpenFileDialog(MainWidget.FILEFILTER_SIMULATION);

                if (!filePath.equals("")) {
                    int simId = projectManager.importSimulation(filePath);
                    if (simId != -1) {
                        ProjectPanel.this.panelCompare.addSimulation(simId);
                    }
                }
            }
        });
        buttonClose.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                Model.removeProject(projectId);
                MainWidget.getTabbedPane().remove(ProjectPanel.this);
                ((DefaultTreeModel) MainWidget.getTreeView().getModel()).removeNodeFromParent(ProjectPanel.this.node);
            }
        });
    }

    /**
     * Create a ProjectPanel
     * @param projectName: the name of the project
     * @param node: the node representing the panel in the main tree
     */
    public ProjectPanel(String projectName, SifisyTreeNode node) {
        super(new BorderLayout());
        this.projectName = projectName;
        this.node = node;
        projectManager = new ProjectManager(projectName, this);
        initComponents();

        add(toolBar, BorderLayout.NORTH);
        add(tabbedPane, BorderLayout.CENTER);

        tabbedPane.addTab(Model.translate("settings"),
                new ImageIcon(getClass().getResource("icons/small/settings.png")),
                panelCompare);
        SifisyTreeNode resultsNode = new SifisyTreeNode(Model.translate("settings"));
        node.add(resultsNode);
        resultsNode.setTabIndex(0);
        MainWidget.updateTreeView();
    }

    /**
     * Returns the number of simulations in the project
     * @return number of simulations in the project
     */
    public int getSimulationsCount() {
        return tabbedPane.getTabCount();
    }

    /**
     * Return the tabbed pane
     * @return the tabbed pane
     */
    public JTabbedPane getTabbedPane() {
        return tabbedPane;
    }

    /**
     * Activate or deactivate user interaction with the panel
     * @param active: if true it activates user interaction
     */
    public void lockWindowsDialog(boolean active) {
        if (active == true) {
            this.setEnabled(false);
            //System.out.println("Attendere attivato");
        } else {
            this.setEnabled(true);
            //System.out.println("Attendere disattivCompletata la gestione dei file multipli (versione non definitiva).ato");
        }
    }

    /**
     * Notify the panel with a new id for the project
     * @param projectId: the new id of the project
     */
    public void notifyPanel(int projectId) {
        lockWindowsDialog(false);
        this.projectId = projectId;
    }

    /**
     * Notify the panel with a message
     * @param message: the message to print
     */
    public void notifyPanel(String message) {
        lockWindowsDialog(false);
        panelCompare.printMessage(message);
    }

    /**
     * Notify the panel and activate user interation
     */
    public void notifyPanel() {
        lockWindowsDialog(false);
    }

    /**
     * Return the id of the project
     * @return the id of the project
     */
    public int getProjectId() {
        return projectId;
    }

    /**
     * Set the id of the current project
     * @param projectId
     */
    public void setProjectId(int projectId) {
        this.projectId = projectId;
    }


    /**
     * Return the controller of this panel
     * @return the controller
     */
    public ProjectManager getProjectManager() {
        return projectManager;
    }

    /**
     * Show the result panel
     */
    public void showResults() {
        panelCompare.showResults();
    }

    /**
     * Show the configuration panel
     */
    public void showConfig() {
        panelCompare.showConfig();
    }

    /**
     * Add a simulation to this panel
     * @param simulationName: the name of the simulation to add
     * @param simulationNode: the node of the simulation to add
     * @return the new simulation panel
     */
    public SimulationPanel addSimulation(String simulationName,
            SifisyTreeNode simulationNode) {
        MainWidget.addNodeToTree(simulationNode, node);
        SimulationPanel simPanel = new SimulationPanel(simulationName, simulationNode,
                this);
        //SimulationPanel simPanel = MainWidget.createSimulation(simulationName, this);
        getTabbedPane().addTab(simulationName,
                new ImageIcon(this.getClass().getResource("icons/small/simulation.png")),
                simPanel);
        projectManager.addSimulation(simPanel.getSimulationId());
        panelCompare.addSimulation(simPanel.getSimulationId());
        simulationNode.setTabIndex(getTabbedPane().getTabCount() - 1);
        //comparePanel.importSimulation(simPanel.getSimId());
        //getTabbedPane().setSelectedIndex(getTabbedPane().getTabCount() - 1);

        return simPanel;
    }

    /**
     * Remove a simulation from the project
     * @param simPanel: the simulation panel to remove
     */
    public void removeSimulation(SimulationPanel simPanel) {
        projectManager.removeSimulation(simPanel.getSimulationId());
        panelCompare.removeSimulation(simPanel.getSimulationId());
        getTabbedPane().remove(simPanel);
    }

    /**
     * Return the scroll pane containing the results of the project
     * @return the scroll pane
     */
    public JScrollPane getResults() {
        return panelCompare.getTableScrollPane();
    }

    /**
     * Return the height of the results table
     * @return the height of the results table
     */
    public int getTableHeight() {
        return panelCompare.getTableHeight();
    }

    /**
     * Reload the information of the project in the view
     */
    public void refreshFields() {
        projectName = Model.getProject(projectId).getName();

        MainWidget.getTabbedPane().setTitleAt(node.getTabIndex(),
                projectName);
        MainWidget.getTabbedPane().updateUI();

        node.setName(projectName);
        MainWidget.updateTreeView();

        for (int i = 1; i < getTabbedPane().getTabCount(); i++) {
            ((SimulationPanel) getTabbedPane().getComponentAt(i)).refreshFields();
        }
    }

    /**
     * Return the node of the tree view representing this panel
     * @return the node of this panel
     */
    public SifisyTreeNode getNode() {
        return node;
    }
}

/**
 * ProjectComparePanel is a panel for managing simulations and project results
 * into a ProjectPanel
 * @author Luca Zanini
 * @version %I%, %G%
 * @createData 24/05/2010
 */
class ProjectComparePanel extends JPanel {

    /**
     * Internal class that provides JCheckBox in a JList
     */
    class CheckListRenderer extends JCheckBox implements ListCellRenderer {

        private int simId;

        /**
         * Default constructor for CheckListRenderer
         */
        public CheckListRenderer() {
            super();
        }

        /**
         * Create a new CheckListRenderer item
         * @param displayText: the text to display
         * @param simulationId: the id of the simulation
         */
        public CheckListRenderer(String displayText, int simulationId) {
            super(displayText);
            simId = simulationId;
        }

        public Component getListCellRendererComponent(JList list, Object value,
                int index, boolean isSelected, boolean hasFocus) {
            setEnabled(list.isEnabled());
            setSelected(((JCheckBox) value).isSelected());
            setFont(list.getFont());
            setBackground(list.getBackground());
            setForeground(list.getForeground());
            setText(((JCheckBox) value).getText());
            return this;
        }

        /**
         * Return the id of the simulation of this CheckListRenderer
         * @return the id of the simulation
         */
        public int getSimId() {
            return simId;
        }
    }

    /**
     * Internal class that provides a custom renderer for JTable; this class
     * implements the ability to change the background color of a cell
     */
    class SifisyTableCellRenderer extends DefaultTableCellRenderer {
        private Color cellColor;

        /**
         * Create a new SifisyTableCellRenderer
         * @param cellColor the background color of the cell
         */
        public SifisyTableCellRenderer(Color cellColor) {
            this.cellColor = cellColor;
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value,
                boolean isSelected, boolean hasFocus, int row, int column) {
            Component cell = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);

            if (((SifisyTableCell)value).isColored())
                cell.setBackground(cellColor);
            else
                cell.setBackground(table.getBackground());

            return cell;
        }

        /**
         * Return the background color of the cell
         * @return the background color
         */
        public Color getCellColor() {
            return cellColor;
        }

        /**
         * Set the background color of the cell
         * @param color the desired background color
         */
        public void setCellColor(Color color) {
            cellColor = color;
        }
    }

    /**
     * Internal class that provides a custom JTable cell for storing it's
     * displayed value and if it has to be colored
     */
    class SifisyTableCell extends Component {
        private String value;
        private boolean isColored;

        /**
         * Create a new SifisyTableCell
         * @param value the value to store
         * @param isColored true if the cell has to be colored
         */
        SifisyTableCell(String value, boolean isColored) {
            this.value = value;
            this.isColored = isColored;
        }

        /**
         * Return true if the cell is colored
         * @return true if the cell is colored
         */
        public boolean isColored() {
            return isColored;
        }

        /**
         * Return the value stored in the cell
         * @return the value stored in the cell
         */
        public String getValue() {
            return value;
        }

        @Override
        public String toString() {
            return value;
        }
    }


    private static final String CONFIG_PANEL = "configPanel";
    private static final String RESULTS_PANEL = "resultsPanel";
    private ProjectPanel parentPanel;
    private JToolBar toolBar;
    JButton buttonConfigProject;
    JButton buttonStartProject;
    private JButton buttonExportPdf;
    private JPanel panelInner;
    private JPanel panelConfig;
    private JList listSelectedSimulations;
    private DefaultListModel listModel;
    private JScrollPane listScrollPane;
    private JScrollPane infoScrollPane;
    private JTextArea textInfo;
    private JPanel panelResults;
    private JTable tableResults;
    private DefaultTableModel tableModel;
    private JScrollPane tableScrollPane;

    /**
     * Create a new ProjectComparePanel
     * @param parentPanel: the parent ProjectPanel
     */
    public ProjectComparePanel(ProjectPanel parentPanel) {
        super(new BorderLayout());

        this.parentPanel = parentPanel;
        initComponents();

        add(toolBar, BorderLayout.NORTH);
        add(panelInner, BorderLayout.CENTER);

        ((CardLayout) panelInner.getLayout()).show(panelInner, "configPanel");
    }

    private void initComponents() {
        toolBar = new JToolBar();
        toolBar.setFloatable(false);
        buttonConfigProject = new JButton(Model.translate("config"));
        buttonConfigProject.setIcon(new ImageIcon(getClass().getResource("icons/small/projectConfig.png")));
        buttonStartProject = new JButton(Model.translate("startComparison"));
        buttonStartProject.setIcon(new ImageIcon(getClass().getResource("icons/small/startProject.png")));
        buttonExportPdf = new JButton(Model.translate("export"));
        buttonExportPdf.setIcon(new ImageIcon(getClass().getResource("icons/small/exportPdf.png")));
        toolBar.add(buttonConfigProject);
        toolBar.add(buttonStartProject);
        toolBar.add(buttonExportPdf);

        panelInner = new JPanel(new CardLayout());

        panelConfig = new JPanel();
        panelConfig.setLayout(new BoxLayout(panelConfig, BoxLayout.Y_AXIS));
        panelConfig.setBorder(new TitledBorder(Model.translate("projectPanelDesc01")));
        listSelectedSimulations = new JList();
        listModel = new DefaultListModel();
        listSelectedSimulations.setModel(listModel);
        listSelectedSimulations.setCellRenderer(new CheckListRenderer());
        listScrollPane = new JScrollPane(listSelectedSimulations);
        panelConfig.add(listScrollPane);
        textInfo = new JTextArea(3, 10);
        textInfo.setEditable(false);
        infoScrollPane = new JScrollPane(textInfo);
        infoScrollPane.setBorder(new TitledBorder(Model.translate("information")));
        panelConfig.add(infoScrollPane);

        panelResults = new JPanel(/*new FlowLayout(FlowLayout.LEFT)*/);
        panelResults.setLayout(new BoxLayout(panelResults, BoxLayout.Y_AXIS));
        tableResults = new JTable();
        tableResults.setRowHeight(20);
        tableResults.setEnabled(false);
//        tableResults.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
//        tableResults.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
        tableScrollPane = new JScrollPane(tableResults);

        String[] columnNames = new String[] {
            Model.translate("simulation"),
            "File System",
            Model.translate("averageReading"),
            "Overhead",
            Model.translate("fileSystemSize"),
            Model.translate("maxLengthFile"),
            Model.translate("internalFragm")
        };

        tableModel = new DefaultTableModel(columnNames, 0);
        tableResults.setModel(tableModel);

        for (int i=1; i<tableResults.getColumnCount(); i++)
            tableResults.getColumnModel().getColumn(i)
                    .setCellRenderer(new SifisyTableCellRenderer(Color.GREEN));

//        tableResults.getColumnModel().getColumn(0).setWidth(tableResults.getColumnModel().getColumn(0).getWidth() + 20);
//        tableResults.getColumnModel().getColumn(1).setWidth(tableResults.getColumnModel().getColumn(1).getWidth() + 30);
//        tableResults.getColumnModel().getColumn(2).setWidth(tableResults.getColumnModel().getColumn(2).getWidth() + 40);
//        tableResults.getColumnModel().getColumn(4).setWidth(tableResults.getColumnModel().getColumn(4).getWidth() + 30);
//        tableResults.getColumnModel().getColumn(5).setWidth(tableResults.getColumnModel().getColumn(5).getWidth() + 40);
//        tableResults.getColumnModel().getColumn(6).setWidth(tableResults.getColumnModel().getColumn(6).getWidth() + 20);

        panelResults.add(tableScrollPane);

        panelInner.add(panelConfig, CONFIG_PANEL);
        panelInner.add(panelResults, RESULTS_PANEL);

        buttonConfigProject.setEnabled(false);
        buttonStartProject.setEnabled(false);
        buttonExportPdf.setEnabled(false);

        // Adding listeners
        buttonConfigProject.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                showConfig();
            }
        });
        buttonStartProject.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                parentPanel.getProjectManager().startCompare();
            }
        });
        buttonExportPdf.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                String filePath = MainWidget.showSaveFileDialog(MainWidget.FILEFILTER_PDF);

                if (!filePath.equals("")) {
                    parentPanel.getProjectManager().exportPdf(filePath);
                }
            }
        });

        listSelectedSimulations.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                JList list = (JList) e.getSource();

                int index = list.locationToIndex(e.getPoint());
                if (index != -1) {
                    CheckListRenderer itemClicked = (CheckListRenderer) list.getModel().getElementAt(index);
                    itemClicked.setSelected(!itemClicked.isSelected());
                    list.repaint(list.getCellBounds(index, index));
                    if (itemClicked.isSelected()) {
                        parentPanel.getProjectManager().addCompare(itemClicked.getSimId());
                    }
                    else {
                        parentPanel.getProjectManager().removeCompare(itemClicked.getSimId());
                    }
                    if (activeSimulations() > 1)
                        ProjectComparePanel.this.buttonStartProject.setEnabled(true);
                    else
                        ProjectComparePanel.this.buttonStartProject.setEnabled(false);
                }
            }

            private int activeSimulations() {
                int count = 0;

                for (int i=0; i<listModel.size(); i++)
                    if (((CheckListRenderer)listModel.get(i)).isSelected())
                        count++;

                return count;
            }
        });
    }

    /**
     * Return the scroll pane containing the results of the project
     * @return the results scroll pane
     */
    public JScrollPane getTableScrollPane() {
        return tableScrollPane;
    }

    /**
     * Add a simulation to compare
     * @param simulationId: the id of the simulation to compare
     */
    public void addSimulation(int simulationId) {
        CheckListRenderer simItem = new CheckListRenderer(Model.getSimulationName(simulationId), simulationId);
        simItem.setSelected(true);
        parentPanel.getProjectManager().addCompare(simulationId);
        listModel.addElement(simItem);
        if (listModel.getSize() > 1) {
            buttonStartProject.setEnabled(true);
        }
    }

    /**
     * Remove a simulation from the project
     * @param simulationId: the id of the simulation to remove
     */
    public void removeSimulation(int simulationId) {
        boolean found = false;

        CheckListRenderer simItem;
        for (int i = 0; i < listModel.getSize() && !found; i++) {
            simItem = (CheckListRenderer) listModel.getElementAt(i);
            if (simItem.getSimId() == simulationId) {
                found = true;
                listModel.remove(i);
            }
        }
        buttonStartProject.setEnabled(true);
        if (listModel.getSize() < 2) {
            buttonStartProject.setEnabled(false);
        }
    }

    /**
     * Return the height of the results table
     * @return the height of the results table
     */
    public int getTableHeight() {
        return tableResults.getHeight() + 17;
    }

    /**
     * Load the results of the project into the results table
     */
    public void fillResultsTable() {
        int projectId = parentPanel.getProjectId();
        int selectedSimsCount = Model.getProject(projectId).getCompare().size();
        Vector<Integer> bestCompare = Model.getProject(projectId).getBestCompare();

        // cleaning results table
        tableModel.setRowCount(0);

        for (int i = 0; i < selectedSimsCount; i++) {
            Vector<SifisyTableCell> row = new Vector<SifisyTableCell>();
            int actualSimId = Model.getProject(projectId).getCompare().elementAt(i);

            row.add(new SifisyTableCell(Model.getSimulationName(actualSimId), false)); // nome simulazione
            row.add(new SifisyTableCell(Model.getFsType(actualSimId), false)); // file system
            if (Model.getChooseAdvanced(actualSimId)) {
                row.add(new SifisyTableCell(String.valueOf(Model.getAverageReading(actualSimId) + " ms"), false)); // tempo medio lettura
            } else {
                row.add(new SifisyTableCell("", false));
            }
            row.add(new SifisyTableCell(String.valueOf(Model.getInflationIndex(actualSimId)),
                    bestCompare.elementAt(0).intValue() == actualSimId)); // overhead
            row.add(new SifisyTableCell(String.valueOf(Model.getPartitionDimension(actualSimId)) + " "
                    + exponentToString(Model.getPartitionExponent(actualSimId)),
                    bestCompare.elementAt(2).intValue() == actualSimId)); // dimensione
            row.add(new SifisyTableCell(String.valueOf(Model.getMaxLenghtFile(actualSimId)),
                    bestCompare.elementAt(1).intValue() == actualSimId)); // grandezza massima file
            row.add(new SifisyTableCell(String.valueOf(Model.getInternalFragmentation(actualSimId)),
                    bestCompare.elementAt(3).intValue() == actualSimId)); // frammentazione interna
            tableModel.addRow(row);
        }
    }

    /**
     * Convert an int exponent to its representing String
     * @param exponent: the exponent to convert
     * @return the String representing the exponent
     */
    private String exponentToString(int exponent) {
        String value = "";

        switch (exponent) {
            case 0: {
                value = "Byte";
                break;
            }
            case 10: {
                value = "KB";
                break;
            }
            case 20: {
                value = "MB";
                break;
            }
            case 30: {
                value = "GB";
                break;
            }
            case 40: {
                value = "TB";
                break;
            }
        }

        return value;
    }

    void showResults() {
        fillResultsTable();
        ((CardLayout) panelInner.getLayout()).show(panelInner, RESULTS_PANEL);
        buttonConfigProject.setEnabled(true);
        buttonStartProject.setEnabled(false);
        buttonExportPdf.setEnabled(true);
    }

    void showConfig() {
        ((CardLayout) panelInner.getLayout()).show(panelInner, CONFIG_PANEL);
        buttonConfigProject.setEnabled(false);
        buttonStartProject.setEnabled(true);
        buttonExportPdf.setEnabled(false);
        if (listModel.getSize() < 2) {
            buttonStartProject.setEnabled(false);
        }
    }

    void printMessage(String message) {
        textInfo.setText("");

        textInfo.setText(message);
    }
}
